package org.lex.swing.tree;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.regex.Pattern;

import javax.swing.tree.DefaultMutableTreeNode;


import org.lex.reflect.browsing.model.Node;
import org.lex.utils.BinarySearch;
import org.lex.utils.Range;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JTreeUtils {
	private Logger log = LoggerFactory.getLogger(JTreeUtils.class);

	public void buildFileTree(DefaultMutableTreeNode root, Collection<File> files) {
		Pattern p = Pattern.compile(Pattern.quote(File.separator));
		for (File file : files) {
			String fullPath = null;
			try {
				fullPath = file.getCanonicalPath();
			} catch (IOException e) {
				file.getAbsolutePath();
			}
			String[] path = p.split(fullPath);
			binaryInsertNode(root, path);
		}
	}

	public <T extends Comparable<T>> void binaryInsertNode(DefaultMutableTreeNode root, T[] path) {
		if (null == path || 0 == path.length) {
			log.warn("TreePath=" + path + " NOT valid");
			return;
		}
		int i = 0;
		DefaultMutableTreeNode current = root;
		while (i < path.length) {
			T name = path[i];
			DefaultMutableTreeNode node = findChild(current, name);
			if (null == node) {
				// add "name" to tree;
				Node.Type type = null;
				if (i == path.length - 1) {
					type = Node.Type.CLASS;
				} else {
					type = Node.Type.PACKAGE;
				}
				node = new Node(name, type);
				int pos = BinarySearch.search(JTreeUtils.<T> range(node), name);
				if (pos > 0) {
					log.warn("WARNING: " + name + " already exist!");
				}
				if (pos < 0) {
					pos = -pos - 1;
				}
				current.insert(node, pos);
			}
			current = node;
			i++;
		}
	}

	private static <T> Range<T> range(final DefaultMutableTreeNode node) {
		return new Range<T>() {
			private int endPos = node.getChildCount();

			@Override
			public int startPosition() {
				return 0;
			}

			@Override
			public int endPosition() {
				return endPos;
			}

			@Override
			@SuppressWarnings("unchecked")
			public T getValue(int index) {
				DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(index);
				return (T) child.getUserObject();
			}
		};
	}

	private <T> DefaultMutableTreeNode findChild(DefaultMutableTreeNode root, T child) {
		int count = root.getChildCount();
		for (int i = 0; i < count; i++) {
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
			if (eq(child, node.getUserObject())) {
				return node;
			}
		}
		return null;
	}

	private static <T> boolean eq(T a, T b) {
		if (a == b) {
			return true;
		} else if (null == a) {
			if (null == b) {
				return true;
			} else {
				return false;
			}
		} else {
			return a.equals(b);
		}
	}
}
